//===========================================================================//
// File:	rotation.hh                                                      //
// Contents: Interface specification for rotation classes                    //
//---------------------------------------------------------------------------//
// Copyright (C) Microsoft Corporation. All rights reserved.                 //
//===========================================================================//

#pragma once

#include "Stuff.hpp"
#include "Angle.hpp"
#include "Vector3D.hpp"

namespace Stuff {
	class UnitQuaternion;
	class YawPitchRoll;
	class EulerAngles;
}

#if !defined(Spew)
	void
		Spew(
			const char* group,
			const Stuff::EulerAngles &angle
		);
	void
		Spew(
			const char* group,
			const Stuff::YawPitchRoll &angle
		);
	void
		Spew(
			const char* group,
			const Stuff::UnitQuaternion &angle
		);
#endif

namespace Stuff {

	class UnitQuaternion;
	class Origin3D;
	class YawPitchRoll;
	class UnitVector3D;
	class LinearMatrix4D;

	//##########################################################################
	//#########################    EulerAngles     #############################
	//##########################################################################

	class EulerAngles
	{
	public:
		Radian
			pitch,
			yaw,
			roll;

		static const EulerAngles
			Identity;

		//
		// Constructors
		//
		EulerAngles()
			{}
		EulerAngles(
			const Radian &p,
			const Radian &y,
			const Radian &r
		)
			{pitch = p; yaw = y; roll = r;}
		EulerAngles(const EulerAngles &a)
			{pitch = a.pitch; yaw = a.yaw; roll = a.roll;}
		explicit EulerAngles(const YawPitchRoll &angles)
			{*this = angles;}
		explicit EulerAngles(const UnitQuaternion &quaternion)
			{*this = quaternion;}
		explicit EulerAngles(const LinearMatrix4D &matrix)
			{*this = matrix;}
		explicit EulerAngles(const Origin3D &origin)
			{*this = origin;}

		//
		// Assignment operators
		//
		EulerAngles&
			operator=(const EulerAngles &angles)
				{
					Check_Pointer(this); Check_Object(&angles);
					pitch = angles.pitch; yaw = angles.yaw; roll = angles.roll;
					return *this;
				}
		EulerAngles&
			operator=(const YawPitchRoll &angles);
		EulerAngles&
			operator=(const UnitQuaternion &quaternion);
		EulerAngles&
			operator=(const LinearMatrix4D &matrix);
		EulerAngles&
			operator=(const Origin3D &p);

		//
		// "Close-enough" comparators
		//
		friend bool
			Small_Enough(
				const EulerAngles &a,
				Scalar e=SMALL
			);
		bool
			operator!() const
				{return Small_Enough(*this);}

		friend bool
			Close_Enough(
				const EulerAngles &a1,
				const EulerAngles &a2,
				Scalar e=SMALL
			);
		bool
			operator==(const EulerAngles& a) const
				{return Close_Enough(*this,a,SMALL);}
		bool
			operator!=(const EulerAngles& a) const
				{return !Close_Enough(*this,a,SMALL);}

		//
		// Axis index operators
		//
		static int GetMemberCount(void)
			{
				return 3;
			}

		const Radian&
			operator[](size_t index) const
				{
					Check_Pointer(this);
					Verify(static_cast<unsigned>(index) <= Z_Axis);
					return (&pitch)[index];
				}
		Radian&
			operator[](size_t index)
				{
					Check_Pointer(this);
					Verify(static_cast<unsigned>(index) <= Z_Axis);
					return (&pitch)[index];
				}

		//
		// Template support
		//
		EulerAngles&
			Lerp(
				const EulerAngles& v1,
				const EulerAngles& v2,
				Scalar t
			);

		//
		// Support functions
		//
		EulerAngles&
			Normalize();

		#if !defined(Spew)
			friend void
				::Spew(
					const char* group,
					const EulerAngles &angle
				);
		#endif

		//
		// Test functions
		//
		void
			TestInstance() const
				{}
		static bool
			TestClass();
	};

	//##########################################################################
	//#########################    YawPitchRoll     ############################
	//##########################################################################

	class YawPitchRoll
	{
	public:
		Radian
			yaw,
			pitch,
			roll;

		static const YawPitchRoll
			Identity;

		//
		// Constructors
		//
		YawPitchRoll()
			{}
		YawPitchRoll(
			const Radian &y,
			const Radian &p,
			const Radian &r
		)
			{pitch = p; yaw = y; roll = r;}
		YawPitchRoll(const YawPitchRoll &angles)
			{pitch = angles.pitch; yaw = angles.yaw; roll = angles.roll;}
		explicit YawPitchRoll(const EulerAngles &angles)
			{*this = angles;}
		explicit YawPitchRoll(const UnitQuaternion &quaternion)
			{*this = quaternion;}
		explicit YawPitchRoll(const LinearMatrix4D &matrix)
			{*this = matrix;}
		explicit YawPitchRoll(const Origin3D &origin)
			{*this = origin;}

		//
		// Assignment operators
		//
		YawPitchRoll&
			operator=(const YawPitchRoll &angles)
				{
					Check_Pointer(this); Check_Object(&angles);
					pitch = angles.pitch; yaw = angles.yaw; roll = angles.roll;
					return *this;
				}
		YawPitchRoll&
			operator=(const EulerAngles &angles);
		YawPitchRoll&
			operator=(const UnitQuaternion &quaternion);
		YawPitchRoll&
			operator=(const LinearMatrix4D &matrix);
		YawPitchRoll&
			operator=(const Origin3D &p);


		static int GetMemberCount(void)
			{
				return 3;
			}

		const Radian&
			operator[](size_t index) const
				{
					Check_Pointer(this);
					Verify(static_cast<unsigned>(index) <= Z_Axis);
					return (&yaw)[index];
				}
		Radian&
			operator[](size_t index)
				{
					Check_Pointer(this);
					Verify(static_cast<unsigned>(index) <= Z_Axis);
					return (&yaw)[index];
				}


		//
		// "Close-enough" comparators
		//
		friend bool
			Small_Enough(
				const YawPitchRoll &a,
				Scalar e=SMALL
			);
		bool
			operator!() const
				{return Small_Enough(*this);}

		friend bool
			Close_Enough(
				const YawPitchRoll &a1,
				const YawPitchRoll &a2,
				Scalar e=SMALL
			);
		bool
			operator==(const YawPitchRoll& a) const
				{return Close_Enough(*this,a);}
		bool
			operator!=(const YawPitchRoll& a) const
				{return !Close_Enough(*this,a);}

		//
		// Template support
		//
		YawPitchRoll&
			Lerp(
				const YawPitchRoll& v1,
				const YawPitchRoll& v2,
				Scalar t
			);

		//
		// Support functions
		//
		YawPitchRoll&
			Normalize();
		#if !defined(Spew)
			friend void
				::Spew(
					const char* group,
					const YawPitchRoll& angle
				);
		#endif
		YawPitchRoll&
			AlignWithVector(const Vector3D &v);

		//
		// Test functions
		//
		void
			TestInstance() const
				{}
	};

	//##########################################################################
	//#########################    UnitQuaternion    ###############################
	//##########################################################################

	class UnitQuaternion
	{
	public:
		static const UnitQuaternion
			Identity;

		static void
			InitializeClass();
		static void
			TerminateClass();

		DECLARE_TIMER(static, SlerpTime);
		static DWORD
			SlerpCount;

		Scalar
			x,
			y,
			z,
			w;

		//
		// Constructors
		//
		UnitQuaternion()
			{}
		UnitQuaternion(
			Scalar x,
			Scalar y,
			Scalar z,
			Scalar w
		)
			{
				Check_Pointer(this);
				this->x = x; this->y = y; this->z = z; this->w = w;
				Check_Object(this);
			}

		//
		// Assignment operators
		//
		UnitQuaternion&
			operator=(const UnitQuaternion &q)
				{
					Check_Pointer(this); Check_Object(&q);
					x = q.x; y = q.y; z = q.z; w = q.w; return *this;
				}
		UnitQuaternion&
			operator=(const EulerAngles &angles);
		UnitQuaternion&
			operator=(const YawPitchRoll &angles);
		UnitQuaternion&
			operator=(const LinearMatrix4D &matrix);
		UnitQuaternion&
			operator=(const Origin3D &p);
		UnitQuaternion&
			operator=(const Vector3D &v);


		//
		// "Close-enough" comparators
		//
		friend bool
			Small_Enough(
				const UnitQuaternion &q,
				Scalar e=SMALL
			)
				{Check_Object(&q); return Close_Enough(q.w,1.0f,e);}
		bool
			operator!() const
				{return Small_Enough(*this,SMALL);}

		friend bool
			Close_Enough(
				const UnitQuaternion &a1,
				const UnitQuaternion &a2,
				Scalar e=SMALL
			);
		bool
			operator==(const UnitQuaternion& a) const
				{return Close_Enough(*this,a,SMALL);}
		bool
			operator!=(const UnitQuaternion& a) const
				{return !Close_Enough(*this,a,SMALL);}

		//
		// Axis index operators
		//
		const Scalar&
			operator[](size_t index) const
				{
					Check_Pointer(this);
					Verify(static_cast<unsigned>(index) <= W_Axis);
					return (&x)[index];
				}
		Scalar&
			operator[](size_t index)
				{
					Check_Pointer(this);
					Verify(static_cast<unsigned>(index) <= W_Axis);
					return (&x)[index];
				}

		Scalar
			GetAngle();
		void
			GetAxis(UnitVector3D *axis);

		//
		// Multiplication operators
		//
		UnitQuaternion&
			Multiply(
				const UnitQuaternion &q1,
				const UnitQuaternion &q2
			);
		UnitQuaternion&
			Multiply(
				const UnitQuaternion &q,
				Scalar scale
			);
		UnitQuaternion&
			MultiplyScaled(
				const UnitQuaternion &q1,
				const UnitQuaternion &q2,
				Scalar t
			);

		//
		// Transform functions
		//
		UnitQuaternion&
			Multiply(
				const UnitQuaternion &q,
				const LinearMatrix4D &m
			);
		UnitQuaternion&
			operator*=(const LinearMatrix4D &m);

 		//
		// Template support
		//

#if 0
		UnitQuaternion&
			Lerp(
				const UnitQuaternion& v1,
				const UnitQuaternion& v2,
				Scalar t
			);
#endif

		UnitQuaternion&
			Lerp(
				const UnitQuaternion& p, 
				const UnitQuaternion& q, 
				Scalar t
			);

		UnitQuaternion&
			FastLerp(
				const UnitQuaternion& p, 
				const UnitQuaternion& q, 
				Scalar t
			);
		

		UnitQuaternion&
			Lerp(
				const EulerAngles& v1,
				const EulerAngles& v2,
				Scalar t
			);

		//
		// Miscellaneous functions
		//
		UnitQuaternion&
			Normalize();

		UnitQuaternion&
			FastNormalize();

		UnitQuaternion&
			Subtract(
				const UnitQuaternion &end,
				const UnitQuaternion &start
			);
		UnitQuaternion&
			Subtract(
				const UnitVector3D &end,
				const UnitVector3D &start
			);
		UnitQuaternion&
			Subtract(
				const Vector3D &end,
				const Vector3D &start
			);
#if 0
		UnitQuaternion&
			Combine(
				const EulerAngles& v1,
				Scalar t1,
				const EulerAngles& v2,
				Scalar t2
			);
#endif




		// JSE -------------------------------------------------------
		// ---- UnitQuaternion Slerping and Splining routines 
		// ---- USE AT OWN RISK!!!! 
		// ---- This code is as of yet untested!!!
		//------------------------------------------------------------

		

		UnitQuaternion
			Squad(
				const UnitQuaternion& p, // start quaternion
				const UnitQuaternion& a, // start tangent quaternion
				const UnitQuaternion& b, // end tangent quaternion
				const UnitQuaternion& q, // end quaternion
				Scalar t
			);

		UnitQuaternion
			SquadRev(
				Scalar angle,			// angle of rotation 
				const Point3D& axis,	// the axis of rotation 
				const UnitQuaternion& p,	// start quaternion 
				const UnitQuaternion& a, 	// start tangent quaternion 
				const UnitQuaternion& b, 	// end tangent quaternion 
				const UnitQuaternion& q,	// end quaternion 
				Scalar t 				// parameter, in range [0.0,1.0] 
			);
		
		UnitQuaternion& 
			MakeClosest(const UnitQuaternion& qto);

		Scalar
			Dot(
				const UnitQuaternion& p, 
				const UnitQuaternion& q
			);
		
		void
			Negate()
		{
			x = -x;
			y = -y;
			z = -z;
			w = -w;
		}

		UnitQuaternion&
			Inverse(
				const UnitQuaternion& q
			);

		UnitQuaternion&
			Exp(
				const UnitQuaternion& q
			);

		UnitQuaternion&
			Divide(
				const UnitQuaternion& p, 
				const UnitQuaternion& q
			);

		UnitQuaternion&
			LnDif(
				const UnitQuaternion& p, 
				const UnitQuaternion& q
			);

		UnitQuaternion&
			LogN(
				const UnitQuaternion& q
			);

		UnitQuaternion
			CompA(
				const UnitQuaternion& qprev,
				const UnitQuaternion& q,
				const UnitQuaternion& qnext
			);

		UnitQuaternion&
			Orthog(
				const UnitQuaternion& p, 
				const Point3D& axis
			);

		// END JSE ---------------------------------------
		//------------------------------------------------






		//
		// Support functions
		//
		#if !defined(Spew)
			friend void
				::Spew(
					const char* group,
					const UnitQuaternion &quat
				);
		#endif
		void
			TestInstance() const;
		static bool
			TestClass();
	};

}

namespace MemoryStreamIO {

	inline Stuff::MemoryStream&
		Read(
			Stuff::MemoryStream* stream,
			Stuff::EulerAngles *output
		)
			{return stream->ReadBytes(output, sizeof(*output));}
	inline Stuff::MemoryStream&
		Write(
			Stuff::MemoryStream* stream,
			const Stuff::EulerAngles *input
		)
			{return stream->WriteBytes(input, sizeof(*input));}

	inline Stuff::MemoryStream&
		Read(
			Stuff::MemoryStream* stream,
			Stuff::YawPitchRoll *output
		)
			{return stream->ReadBytes(output, sizeof(*output));}
	inline Stuff::MemoryStream&
		Write(
			Stuff::MemoryStream* stream,
			const Stuff::YawPitchRoll *input
		)
			{return stream->WriteBytes(input, sizeof(*input));}

	inline Stuff::MemoryStream&
		Read(
			Stuff::MemoryStream* stream,
			Stuff::UnitQuaternion *output
		)
			{return stream->ReadBytes(output, sizeof(*output));}
	inline Stuff::MemoryStream&
		Write(
			Stuff::MemoryStream* stream,
			const Stuff::UnitQuaternion *input
		)
			{return stream->WriteBytes(input, sizeof(*input));}

}
